AWS CLIの実装からEC2 Instance Connect Endpointを読み解いてみた

AWS CLIの実装からEC2 Instance Connect Endpointを読み解いてみた

Clock Icon2023.06.14

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

しばたです。

先日EC2 Instance Connectの新機能となるEC2 Instance Connect Endpointが公開されDevelopersIOでもいくつか記事が公開されています。

基本的な機能や使い方についてはこれらの記事をご覧いただくと良いでしょう。

ただ、私個人としてはこの機能の実装や従来のEC2 Instance Connectとの違いが気になりいろいろ調べてみたのでその結果を共有したいと思います。

EC2 Instance Connect Endpoint概要

改めてこのEC2 Instance Connect Endpointの概要を解説しておきます。
サービスの構成としては下図の様になり、ユーザーVPC内にVPC Endpointを作成することで「AWS外部からEC2 Instance Connect Endpoint Service → VPC Endpoint → 対象EC2インスタンス」の経路で接続可能になります。

(https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/Connect-using-EC2-Instance-Connect-Endpoint.html より引用)

VPC Endpointが通信の抜け道となることでPrivateなサブネットに対してもSSH接続できる様になるわけです。

従来のEC2 Instance Connectとの違い

従来のEC2 Instance ConnectはPublicに公開されたLinux EC2インスタンスに対しIAMの権限でアクセス制御を行うものであり通信経路に関する設定は一切出てきません。
専用のエージェントプログラムを使いインスタンスメタデータを介したSSH鍵の連携がメイン機能となっています。

https://dev.classmethod.jp/articles/ec2-instance-connect/

サービスとしてはどちらもEC2 Instance Connectに属しますが内容は全くの別物です。

aws ec2-instance-connect open-tunnel コマンドとその実装

ドキュメントによればAWS CLIでEC2 Instance Connect Endpointを利用する際はaws ec2-instance-connect open-tunnelコマンドを使うとされています。

# OpenSSHのProxyコマンドとして利用する場合の実行例
ssh -i my-key-pair.pem ec2-user@i-0123456789example \
    -o ProxyCommand='aws ec2-instance-connect open-tunnel --instance-id i-0123456789example'

本日(2023年6月14日)時点ではまだaws ec2-instance-connect open-tunnelコマンドを含むバージョンは未提供であり動作確認することはできませんでした。

しかしながらGitHubではAWS CLI v2向けにこのコマンドを追加するプルリクエストがマージされておりソースコードを読むことはできたので、ソースコードからこの機能の実装を読み解いていきます。

https://github.com/aws/aws-cli/pull/7971

aws ec2-instance-connect open-tunnelコマンドは単純なAWS REST APIのラッパーではなくAWS CLI独自のカスタマイズがされています。
このコマンドではざっくり以下の作業を行っています。

  • 指定されたEC2インスタンスIDから属するVPC/サブネット情報およびPrivate IPアドレスを取得
  • VPC EndpointのDNS名を取得
    • VPC Endpoint IDが指定されている場合はVPC Endpoint情報を直接取得
    • VPC Endpoint IDが指定されていない場合はEC2のVPC/サブネット情報から探索する (eicefetcher.py)
  • VPC EndpointのDNS名をベースにWebSocketのPre-signed URLを発行する (eicesigner.py, websocket.py)
    • Pre-signed URLのパラメーターで宛先EC2インスタンスのPrivate IPとポート番号を指定
    • WebSocketトンネルの有効期間(max_tunnel_duration)は最長3600秒
    • Pre-signed URL自体の有効期間(expires_in)は60秒
  • 発行されたPre-singed URLに接続しEC2インスタンス用のトンネルを張る

open-tunnelの名前の通りWebSocketトンネルを張るまでがこのコマンドの仕事になっています。
このため前述のコマンド例でssh -i my-key-pair.pemとある様に実際にEC2インスタンスに接続する際は鍵指定が必要となっています。

また、REST APIに直接的なOpenTunnelというアクションがあるわけではなく、WebSocketトンネルを張って接続した際にCloudTrailにイベントが記録される様です。[1]
例えばセキュリティグループ設定の不備などにより接続失敗した場合

CloudTrailイベント詳細
{
    // ・・・省略・・・
    "eventSource": "ec2-instance-connect.amazonaws.com",
    "eventName": "OpenTunnel",
    // ・・・省略・・・
    "errorCode": "DialFailure",  
    "errorMessage": "Unable to dial target private ip",
    // ・・・省略・・・
}

といったイベントが記録されていました。
ちなみに接続成功した場合はエラーメッセージ無しのイベントが記録されます。

余談

AWS CLIの実装を調べてみた結果はこんな感じでした。
他にいくつか余談があるので簡単に補足しておきます。

1. マネジメントコンソールから接続する場合の処理

マネジメントコンソールからEC2 Instance Connect Endpointを使った接続をする際はSSH鍵(キーペア)を指定する必要がありません。

(マネジメントコンソールのUIではキーペアの指定欄が無い)

マネジメントコンソールはその実装を読むことが出来ないためCloudTrailイベントからの推測になりますが、どうやら従来のEC2 Instance Connectの機能(SSH鍵の自動生成+連携)とWebSocketトンネルを張るのを併用している様です。

  1. SendSSHPublicKeyイベント (都度ED25519の公開鍵が生成されインスタンスに送られていた)
  2. OpenTunnelイベント (インスタンスに対してトンネルが張られていた)

の順でイベントが記録されていました。

加えてEC2インスタンスにインストールされているEC2 Instance Connect Agentをアンインストールすると接続に失敗する様になったので、この挙動からも両者を併用していると言えるでしょう。

2. aws ec2-instance-connect ssh コマンド

先述のプルリクエストでは新たにaws ec2-instance-connect sshコマンドも増える見込みです。

このコマンドは内部でaws ec2-instance-connect send-ssh-public-keyおよびaws ec2-instance-connect open-tunnelコマンド相当の機能を実行したうえでSSH接続を行ってくれる便利なラッパーコマンドになっています。

ざっくり前項のマネジメントコンソールでの接続と同等の機能を提供してくれるものと考えると分かりやすいでしょう。
(実際にはもっと細かいパラメーター指定が可能な模様)

# WebSocketトンネルを張った上でSSH接続する例
aws ec2-instance-connect ssh --instance-id i-1234567890example --connection-type eice

リリース前なので実際に試せていませんが便利に使えそうな予感がします。

3. 最長トンネル期間を超えた場合

最長トンネル期間(max_tunnel_duration)を超えた場合WebSocketの接続は閉じられます。
マネジメントコンソールからのアクセスで動作確認した際はエラーメッセージ等が出る事無く処理がハングしました。
AWS CLIではエラーメッセージを出力する様に読み取れました。

ちなみに期間延長の仕組みは無い様で、期間を超えた場合は都度接続し直す必要がありそうです。

最後に

以上となります。

改めて本記事の内容をまとめると

  • 従来のEC2 Instance Connectの機能はSSH鍵の自動生成+連携がメインである (SendSSHPublicKey)
  • 今回のEC2 Instance Connect EndpointはEC2インスタンスに対してWebSocketトンネルを張る機能である (OpenTunnel)
  • 両者は別の機能であり併用することも可能である

であることが分かりました。

まだ実際にAWS CLIのコマンドを試すことができず、そのため本記事の内容に誤りがあるかもしれませんのでその点はご了承ください。
新しいバージョンのAWS CLIがリリースされたら実際に動作確認して検証したいと思います。

脚注
  1. ただし、IAM Policyの制御においては ec2-instance-connect:OpenTunnel というアクションが存在します。 ↩︎

Share this article

facebook logohatena logotwitter logo

© Classmethod, Inc. All rights reserved.